package com.bootx.predict;

import com.bootx.predict.pojo.PredictPlugin;
import com.bootx.predict.pojo.PredictionResult;
import com.bootx.predict.pojo.RedPacketBatch;
import com.bootx.predict.pojo.RedPacketRecord;
import com.bootx.predict.util.PredictUtils;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

@Component("chiSquareDeviationPredict")
public class ChiSquareDeviationPredict extends PredictPlugin {

    /**
     * adjustFactor 主要控制偏离率对预测概率的调整力度，一般推荐设置在 0.8 到 1.2 之间，具体选值依据业务特性和历史数据稳定性：
     * 0.8 左右：调整较温和，适合波动不大的数据，防止过度响应偶然偏离。
     * 1.0 左右：中等调整，常用默认值，适合一般场景。
     * 1.2 及以上：调整较强，适合偏离明显且规律稳定的数据，快速放大趋势信号。
     * 调整建议
     *      如果历史奇偶比例波动剧烈，建议调小 adjustFactor，避免预测震荡过大。
     *      如果奇偶比例长期稳定且有明显偏向，调大 adjustFactor，强化趋势判断。
     */
    private final double adjustFactor = 1.0;

    @Override
    public String getName() {
        return "chiSquareDeviationPredict";
    }

    @Override
    public List<PredictionResult> predict(List<RedPacketBatch> list, Set<Integer> indexes) {
        List<RedPacketRecord> history = PredictUtils.parseData(list);
        Map<Integer, List<RedPacketRecord>> grouped = history.stream()
                .collect(Collectors.groupingBy(RedPacketRecord::getIndex));

        List<PredictionResult> results = new ArrayList<>();
        for (int idx : indexes) {
            List<RedPacketRecord> records = grouped.getOrDefault(idx, Collections.emptyList());
            int total = records.size();
            if (total == 0) {
                results.add(new PredictionResult(idx, 0, 0, 0.5, 0));
                continue;
            }

            int oddCount = (int) records.stream().filter(r -> PredictUtils.isAmountOdd(r.getAmount())).count();
            int evenCount = total - oddCount;

            // 卡方偏离检测
            double expected = total / 2.0;
            double chiSquare = Math.pow(oddCount - expected, 2) / expected + Math.pow(evenCount - expected, 2) / expected;

            // 偏离率计算
            double deviation = (oddCount / (double) total) - 0.5;
            double probability = 0.5 + deviation * adjustFactor;

            // 限制范围
            if (probability > 0.99) probability = 0.99;
            if (probability < 0.01) probability = 0.01;

            double avgOpenTime = records.stream().mapToInt(RedPacketRecord::getOpenTime).average().orElse(0);

            results.add(new PredictionResult(idx, total, oddCount, probability, Double.valueOf(avgOpenTime+"").intValue()));
        }
        return results;
    }
}
